/***************************************************************************
 *   This is a part of the NextCAD Web APP.                                *
 *   Copyright (C) AMCAX, All rights reserved.                             *
 ***************************************************************************/

import { pickedFaceData, pickedLineData, pickedVertexData } from '@amcax/base';
import {
  NONE_TITLE_UISCHEMA,
  NumberInputFormat,
  PickInputRJSFFormat,
} from '@app-cad/common/components/command-form/constants';
import { PickableCommandForm } from '@app-cad/common/components/command-form/pickable';
import { useCommand } from '@app-cad/common/hooks/useCommand';
import { useTools } from '@app-cad/common/hooks/useTools';
import { CurvedBaseCommand, CurvedCmdDialogParams } from '@app-cad/types';
import { useAsyncEffect } from 'ahooks';
import i18n from 'i18next';
import { useEffect, useMemo, useState } from 'react';
import './index.scss';

type FormData = {
  plane?: pickedFaceData;
  point1?: pickedVertexData;
  point2?: pickedVertexData;
  tangent1?: pickedLineData;
  tangent2?: pickedLineData;
  constraints?: {
    point?: pickedVertexData;
    tangent?: pickedLineData;
  }[];
  parameter?: number;
  dismode: 'point-param' | 'constraints';
};

type Params = {
  plane: string;
  constraints: {
    point: string;
    tangent: string;
  }[];
  parameter: number;
};

function CommandCurveConic({ title, onClose, props }: CurvedCmdDialogParams) {
  const initData = {
    constraints: [{}, {}, {}],
    parameter: 0.5,
    dismode: 'point-param',
  } as FormData;

  const [formData, setFormData] = useState<FormData>(initData);

  const { picker } = useTools();

  useEffect(() => {
    preview();
  }, [formData]);

  useAsyncEffect(async () => {
    if (props == null) {
      setFormData(initData);
      return;
    }

    const params: Params = props.params;
    const { plane, constraints, parameter } = params;

    await initEditDependency([
      plane,
      ...constraints.flatMap((next) => [next.point, next.tangent]),
    ]);

    setFormData({
      plane: picker.generatePickObjByPickID(plane) as pickedFaceData,
      point1:
        parameter != null
          ? (picker.generatePickObjByPickID(
              constraints[0].point,
            ) as pickedVertexData)
          : null,
      tangent1:
        parameter != null
          ? (picker.generatePickObjByPickID(constraints[0].tangent, {
              showArrow: true,
            }) as pickedLineData)
          : null,
      point2:
        parameter != null
          ? (picker.generatePickObjByPickID(constraints[1].point, {
              showArrow: true,
            }) as pickedVertexData)
          : null,
      tangent2:
        parameter != null
          ? (picker.generatePickObjByPickID(
              constraints[1].tangent,
            ) as pickedLineData)
          : null,
      constraints:
        parameter != null
          ? []
          : constraints.map((c) => ({
              point: picker.generatePickObjByPickID(
                c.point,
              ) as pickedVertexData,
              tangent:
                c.tangent &&
                (picker.generatePickObjByPickID(c.tangent) as pickedLineData),
            })),
      parameter,
      dismode: parameter ? 'point-param' : 'constraints',
    });
  }, [props?.shape]);

  const isFormDataValid = () => {
    const {
      plane,
      constraints,
      parameter,
      point1,
      point2,
      tangent1,
      tangent2,
      dismode,
    } = formData;

    if (plane == null) {
      return false;
    }

    if (
      dismode === 'point-param' &&
      point1 != null &&
      point2 != null &&
      tangent1 != null &&
      tangent2 != null &&
      parameter < 1 &&
      parameter > 0
    ) {
      return true;
    }

    let constraintNum = 0;
    constraints?.forEach(({ point, tangent }) => {
      if (point != null) {
        constraintNum++;
      }
      if (tangent != null) {
        constraintNum++;
      }
    });

    return (
      dismode === 'constraints' &&
      constraints?.every((c) => c.point != null) &&
      constraintNum >= 5
    );
  };

  const checkAndFinishDraw = async () => {
    if (!isFormDataValid()) {
      return;
    }

    await execute(false);

    onClose();
  };

  const preview = async () => {
    clear();
    if (!isFormDataValid()) {
      return;
    }

    await execute(true);
  };

  const {
    initEditDependency,
    initingEdit,
    confirmCommand,
    clear,
    isLoading,
    isPreviewLoading,
  } = useCommand('cmd-curve-conic', {
    onClose,
    finishDraw: checkAndFinishDraw,
    commandName: 'CreateCurve',
    editDxid: props?.shape,
  });

  const execute = (preview: boolean) => {
    const {
      plane,
      constraints,
      parameter,
      point1,
      tangent1,
      point2,
      tangent2,
    } = formData;

    return confirmCommand(
      {
        type: 'Conic2',
        a: {
          plane: plane?.meshId,
          constraints:
            formData.dismode === 'point-param'
              ? [
                  {
                    point: point1.vertexId,
                    tangent: tangent1.edgeId,
                  },
                  {
                    point: point2.vertexId,
                    tangent: tangent2.edgeId,
                  },
                ]
              : constraints.map((c) => ({
                  point: c.point.vertexId,
                  tangent: c.tangent?.edgeId,
                })),
          parameter: formData.dismode === 'point-param' ? parameter : null,
        },
      },
      preview,
    );
  };

  const schema = useMemo(() => {
    const dismodeSchema = {
      type: 'string',
      title: '模式',
      default: 'point-param',
      oneOf: [
        {
          type: 'string',
          title: '两点与参数',
          const: 'point-param',
        },
        {
          type: 'string',
          title: '多点约束',
          const: 'constraints',
        },
      ],
    };
    if (formData.dismode === 'point-param') {
      return {
        title: '两点与参数',
        properties: {
          dismode: dismodeSchema,
          plane: {
            type: 'string',
            format: PickInputRJSFFormat.PickPlane,
          },
          point1: {
            type: 'string',
            format: PickInputRJSFFormat.PickVertex,
          },
          tangent1: {
            type: 'string',
            format: PickInputRJSFFormat.PickLine,
          },
          point2: {
            type: 'string',
            format: PickInputRJSFFormat.PickVertex,
          },
          tangent2: {
            type: 'string',
            format: PickInputRJSFFormat.PickLine,
          },
          parameter: {
            title: '参数',
            type: 'number',
            format: NumberInputFormat.Real,
          },
        },
      };
    }

    if (formData.dismode === 'constraints') {
      return {
        title: '两点与参数',
        properties: {
          dismode: dismodeSchema,
          plane: {
            type: 'string',
            format: PickInputRJSFFormat.PickPlane,
          },
          constraints: {
            type: 'array',
            items: {
              type: 'object',
              properties: {
                point: {
                  title: '点',
                  type: 'string',
                  format: PickInputRJSFFormat.PickVertex,
                },
                tangent: {
                  title: '切线',
                  type: 'string',
                  format: PickInputRJSFFormat.PickLine,
                },
              },
            },
          },
        },
      };
    }

    return schema;
  }, [formData.dismode]);

  return (
    <PickableCommandForm
      disabled={initingEdit}
      onClose={onClose}
      submitLoading={isLoading}
      previewLoading={isPreviewLoading}
      title={title}
      schema={schema}
      formData={formData}
      onChange={(v) => {
        setFormData(v);
      }}
      extraUiSchema={{
        'ui:options': {
          label: false,
          classNames: [
            'command-form command-form_cmd-curve-conic',
            formData.dismode === 'point-param' ? '' : 'command-form-array',
          ].join(' '),
        },
        plane: {
          ...NONE_TITLE_UISCHEMA,
          'ui:placeholder': '请输入支持面',
          'ui:autofocus': true,
        },
        point1: { ...NONE_TITLE_UISCHEMA, 'ui:placeholder': '请输入点1' },
        tangent1: {
          ...NONE_TITLE_UISCHEMA,
          'ui:placeholder': '请输入点1的切线',
          'ui:options': {
            label: false,
            showArrow: true,
          },
        },
        point2: { ...NONE_TITLE_UISCHEMA, 'ui:placeholder': '请输入点2' },
        tangent2: {
          ...NONE_TITLE_UISCHEMA,
          'ui:placeholder': '请输入点2的切线',
          'ui:options': {
            label: false,
            showArrow: true,
          },
        },
        dismode: {
          'ui:classNames': 'command-form__mode-switch',
        },
        constraints: {
          items: {
            tangent: {
              'ui:options': {
                showArrow: true,
              },
            },
          },
        },
      }}
      submitText='完成'
      onSubmit={checkAndFinishDraw}
      noValidate
      isFormDataValid={isFormDataValid()}
    />
  );
}

export const cmdCurveConic: CurvedBaseCommand = {
  id: 'CommandCurveConic',
  label: 'command.curve-conic',
  icon: 'commands/curved/conic',
  create: (label, onClose, props) => (
    <CommandCurveConic
      title={i18n.t(label)}
      onClose={onClose}
      props={props}
    />
  ),
};
